package com.uam.core.config;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;

import javax.net.ssl.SSLContext;

import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.ssl.SSLContexts;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.uam.core.entity.constant.Constant;

import lombok.extern.slf4j.Slf4j;

@Configuration
@Slf4j
public class ElasticsearchConfig {

	@Value("${elasticsearch.hosts}")
	private String addresses = null;

	@Value("${elasticsearch.use.credential}")
	private boolean useCredential = false;

	@Value("${elasticsearch.username}")
	private String username = null;

	@Value("${elasticsearch.password}")
	private String password = null;

	@Value("${elasticsearch.ssl.type}")
	private String sslType = null;

	@Value("${elasticsearch.ssl.password}")
	private String sslPassword = null;

	@Value("${elasticsearch.ssl.path}")
	private String sslPath = null;

	@Value("${elasticsearch.conn.per.route.max}")
	private int connPerRouteMax = 0;

	@Value("${elasticsearch.conn.total.max}")
	private int connTotalMax = 0;

	@Value("${spring.data.elasticsearch.rest.connection-timeout}")
	private int socketTimeout = 0;

	@Bean
	public RestHighLevelClient elasticsearchClient() {
		boolean esUseSsl = false;
		String[] addresses = Constant.SEMICOLON_REGEX.split(this.addresses, 0);
		int length = addresses.length;
		HttpHost[] hosts = new HttpHost[length];
		int i = 0;
		while (i < length) {
			String[] address = Constant.COLON_REGEX.split(addresses[i], 0);
			/* if (!esUseSsl) {
				esUseSsl = "https".equalsIgnoreCase(address[0]);
			} */
			hosts[i++] = new HttpHost(address[1], Integer.parseInt(address[2]), address[0]);
		}

		RestClientBuilder clientBuilder = RestClient.builder(hosts);
		if (this.useCredential) {
			CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
			credentialsProvider.setCredentials(AuthScope.ANY,
					new UsernamePasswordCredentials(this.username, this.password));
			if (esUseSsl) {
				try {
					KeyStore truststore = KeyStore.getInstance(this.sslType);
					truststore.load(new FileInputStream(this.sslPath), this.sslPassword.toCharArray());
					final SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(truststore, null).build();
					clientBuilder.setHttpClientConfigCallback(httpClientConfigCallback -> httpClientConfigCallback
							.setDefaultCredentialsProvider(credentialsProvider).setSSLContext(sslContext)
							.setMaxConnPerRoute(this.connPerRouteMax).setMaxConnTotal(this.connTotalMax));
				} catch (KeyManagementException e) {
					ElasticsearchConfig.log.error(e.getMessage(), e);
				} catch (KeyStoreException e) {
					ElasticsearchConfig.log.error(e.getMessage(), e);
				} catch (NoSuchAlgorithmException e) {
					ElasticsearchConfig.log.error(e.getMessage(), e);
				} catch (CertificateException e) {
					ElasticsearchConfig.log.error(e.getMessage(), e);
				} catch (FileNotFoundException e) {
					ElasticsearchConfig.log.error(e.getMessage(), e);
				} catch (IOException e) {
					ElasticsearchConfig.log.error(e.getMessage(), e);
				}
			} else {
				clientBuilder.setHttpClientConfigCallback(httpClientConfigCallback -> httpClientConfigCallback
						.setDefaultCredentialsProvider(credentialsProvider).setMaxConnPerRoute(this.connPerRouteMax)
						.setMaxConnTotal(this.connTotalMax));
			}
		} else if (esUseSsl) {
			try {
				KeyStore truststore = KeyStore.getInstance(this.sslType);
				truststore.load(new FileInputStream(this.sslPath), this.sslPassword.toCharArray());
				final SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(truststore, null).build();
				clientBuilder.setHttpClientConfigCallback(
						httpClientConfigCallback -> httpClientConfigCallback.setSSLContext(sslContext)
								.setMaxConnPerRoute(this.connPerRouteMax).setMaxConnTotal(this.connTotalMax));
			} catch (KeyManagementException e) {
				ElasticsearchConfig.log.error(e.getMessage(), e);
			} catch (KeyStoreException e) {
				ElasticsearchConfig.log.error(e.getMessage(), e);
			} catch (NoSuchAlgorithmException e) {
				ElasticsearchConfig.log.error(e.getMessage(), e);
			} catch (CertificateException e) {
				ElasticsearchConfig.log.error(e.getMessage(), e);
			} catch (FileNotFoundException e) {
				ElasticsearchConfig.log.error(e.getMessage(), e);
			} catch (IOException e) {
				ElasticsearchConfig.log.error(e.getMessage(), e);
			}
		} else {
			clientBuilder.setHttpClientConfigCallback(httpClientConfigCallback -> httpClientConfigCallback
					.setMaxConnPerRoute(this.connPerRouteMax).setMaxConnTotal(this.connTotalMax));
		}
		clientBuilder.setRequestConfigCallback(
				requestConfigCallback -> requestConfigCallback.setSocketTimeout(this.socketTimeout));
		return new RestHighLevelClient(clientBuilder);
	}

}
